package com.alibaba.citrus.ots.common.log;

import com.alibaba.citrus.ots.common.annotation.FacadeInvoker;
import com.alibaba.citrus.ots.common.exception.*;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;


/**
 * @author: qiaojiange
 * @create: 2021/10/02
 * @Description 日志工具类
 */

@Aspect
@Component
@Order(2)
public class FacadeInvokeHandler {
    /**
     * 竖线
     */
    private static final String VERTICAL_LINE = "|";


    /**
     * 应用名称
     */
    private static String appName = "cr-ots";

    /**
     * 错误信息
     */
    private static String errorMsg = "系统开小差了~";

    @Pointcut("@annotation(com.alibaba.citrus.ots.common.annotation.FacadeInvoker)")
    private void cutMethod() {
    }

    @Around("cutMethod()")
    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
        long startTime = System.currentTimeMillis();
        Log log = Log.getLogger(AopUtils.getClazz(proceedingJoinPoint));
        String argsBody = null;
        Object result = null;
        try {
            argsBody = AopUtils.getArgsString(proceedingJoinPoint);
            result = proceedingJoinPoint.proceed();
        } catch (FacadeException e) {
            log.error(buildErrorInfo(argsBody, proceedingJoinPoint, "Facade", System.currentTimeMillis() - startTime), e);
            return Result.fail(e.getErrorCode(), e.getErrorMessage());
        } catch (FunctionException e) {
            log.error(buildErrorInfo(argsBody, proceedingJoinPoint, "Function", System.currentTimeMillis() - startTime), e);
            return Result.fail(e.getErrorCode(), e.getErrorMessage());
        } catch (ProcessException e) {
            log.error(buildErrorInfo(argsBody, proceedingJoinPoint, "Process", System.currentTimeMillis() - startTime), e);
            return Result.fail(e.getErrorCode(), e.getErrorMessage());
        } catch (ActivityException e) {
            log.error(buildErrorInfo(argsBody, proceedingJoinPoint, "Activity", System.currentTimeMillis() - startTime), e);
            return Result.fail(e.getErrorCode(), e.getErrorMessage());
        } catch (RepositoryException e) {
            log.error(null, buildErrorInfo(argsBody, proceedingJoinPoint, "Repository", System.currentTimeMillis() - startTime), e);
            return Result.fail(e.getErrorCode(), e.getErrorMessage());
        } catch (Throwable throwable) {
            log.error(buildErrorInfo(argsBody, proceedingJoinPoint, "System", System.currentTimeMillis() - startTime), throwable);
            FacadeInvoker facadeInvoker = AopUtils.getTargetMethodAnnotation(proceedingJoinPoint, FacadeInvoker.class);
            if (facadeInvoker != null && facadeInvoker.errorCode() != null) {
                if (throwable instanceof RuntimeException) {
                    FacadeSystemException e = new FacadeSystemException(facadeInvoker.errorCode(), throwable);
                    return Result.fail(e.getErrorCode(), e.getErrorMessage());
                }
            }
            SystemException exception = new SystemException(CommonErrorCode.SYSTEM_ERROR);
            return Result.fail(exception.getErrorCode(), exception.getErrorMessage());
        } finally {
            AccessLogBody accessLogBody = result == null ? AccessLogBody.DEFAULT_ACCESS_LOG(argsBody) : convertResult(argsBody, result);
            log.info(buildAccessLog(proceedingJoinPoint, System.currentTimeMillis() - startTime, accessLogBody));
        }
        return result;
    }

    private String buildAccessLog(ProceedingJoinPoint point, long cost, AccessLogBody accessLogBody) {
        StringBuilder sb = new StringBuilder();
        FacadeInvoker facadeInvoker = AopUtils.getTargetMethodAnnotation(point, FacadeInvoker.class);
        String tag = "-";
        if (hasRemark(facadeInvoker)) {
            tag = StringUtils.isNotBlank(facadeInvoker.remark()) ? facadeInvoker.remark() : facadeInvoker.value();
        }
        return ErrorInfoUtils.buildAccessLog(point, cost, accessLogBody, tag);
    }

    /**
     * 是否有标签
     *
     * @param facadeInvoker
     * @return
     */
    private boolean hasRemark(FacadeInvoker facadeInvoker) {
        return facadeInvoker != null && (StringUtils.isNotBlank(facadeInvoker.remark()) || StringUtils.isNotBlank(facadeInvoker.value()));
    }

    /**
     * 构建错误信息
     *
     *
     * @param argsBody 入参
     * @param point 切点
     * @param level 层次标识
     * @param cost 耗时
     * @return
     */
    private String buildErrorInfo(String argsBody, ProceedingJoinPoint point, String level, long cost) {
        StringBuilder sb = new StringBuilder();
        sb.append(level).append(VERTICAL_LINE)
                //类名
                .append(AopUtils.getSimpleClassName(point)).append(VERTICAL_LINE)
                //方法名
                .append(AopUtils.getMethodName(point)).append(VERTICAL_LINE)
                //处理结果
                .append(false).append(VERTICAL_LINE)
                //耗时
                .append(cost).append(VERTICAL_LINE)
                //入参
                .append(argsBody).append(VERTICAL_LINE)
                //出参
                .append("").append(VERTICAL_LINE)
                //标识
                .append("").append(VERTICAL_LINE)
                //errorCode
                .append(appName).append(VERTICAL_LINE)
                .append(CommonErrorCode.SYSTEM_ERROR).append(VERTICAL_LINE)
                //默认错误信息
                .append(errorMsg);

        return sb.toString();
    }

    private AccessLogBody convertResult(String argsBody, Object result) {
        return AccessLogBody.of(argsBody, JSON.toJSONString(result), true, null, null);
    }

}
